home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 November / Macworld (1999-11).dmg / Updaters / WhiteCap 3.0.4 / WhiteCap Source.sit / WhiteCap Source / Common / math / ExprVirtualMachine.cpp < prev    next >
C/C++ Source or Header  |  1999-07-28  |  11KB  |  461 lines

  1. #include "ExprVirtualMachine.h"
  2.  
  3. #include <math.h>
  4. #include <stdlib.h>
  5.  
  6.  
  7. #define __addInst( opcode, data16 )        long op = (opcode) | (data16);    \
  8.                                         mProgram.Append( &op, 4 );
  9.                     
  10.  
  11. #define REG_IN_USE    0x1
  12. #define REG_USED    0x2
  13.  
  14. #define _fetch( r, val )    switch ( (r) ) {                    \
  15.                                 case 0:        val = FR0;    break;    \
  16.                                 case 1:        val = FR1;    break;    \
  17.                                 case 2:        val = FR2;    break;    \
  18.                                 case 3:        val = FR3;    break;    \
  19.                                 case 4:        val = FR4;    break;    \
  20.                                 case 5:        val = FR5;    break;    \
  21.                                 case 6:        val = FR6;    break;    \
  22.                                 case 7:        val = FR7;    break;    \
  23.                                 default:    val = mVirtFR[ r - NUM_PHYS_REGS ];    \
  24.                             }
  25.                                 /*
  26.                                 case 8:        val = FR8;    break;    \
  27.                                 case 9:        val = FR9;    break;    \
  28.                                 case 10:    val = FR10;    break;    \
  29.                                 case 11:    val = FR11;    break;    \
  30.                                 case 12:    val = FR12;    break;    \
  31.                                 case 13:    val = FR13;    break;    \
  32.                                 case 14:    val = FR14;    break;    \
  33.                                 case 15:    val = FR15;    break;    \
  34.                                 default:    val = 0;    break;    \
  35.                             }*/
  36.                             
  37. #define _store( r, val )    switch ( (r) ) {                    \
  38.                                 case 0:        FR0 = val;    break;    \
  39.                                 case 1:        FR1 = val;    break;    \
  40.                                 case 2:        FR2 = val;    break;    \
  41.                                 case 3:        FR3 = val;    break;    \
  42.                                 case 4:        FR4 = val;    break;    \
  43.                                 case 5:        FR5 = val;    break;    \
  44.                                 case 6:        FR6 = val;    break;    \
  45.                                 case 7:        FR7 = val;    break;    \
  46.                                 default:    mVirtFR[ r - NUM_PHYS_REGS ] = val; \
  47.                             }
  48.                             /*
  49.                                 case 8:        FR8 = val;    break;    \
  50.                                 case 9:        FR9 = val;    break;    \
  51.                                 case 10:    FR10 = val;    break;    \
  52.                                 case 11:    FR11 = val;    break;    \
  53.                                 case 12:    FR12 = val;    break;    \
  54.                                 case 13:    FR13 = val;    break;    \
  55.                                 case 14:    FR14 = val;    break;    \
  56.                                 case 15:    FR15 = val;    break;    \
  57.                             }*/
  58.         
  59.  
  60.  
  61.  
  62. #define _exeFn( r )        switch ( subop ) {                            \
  63.                             case cSQRT:    r = sqrt( r );    break;        \
  64.                             case cATAN:    r = atan( r );    break;        \
  65.                             case cABS:    r = fabs( r );    break;        \
  66.                             case cSIN:    r = sin( r );    break;        \
  67.                             case cCOS:    r = cos( r );    break;        \
  68.                             case cTAN:    r = tan( r );    break;        \
  69.                             case cSGN:    r = ( r >= 0 ) ? 1 : -1;    break;        \
  70.                             case cLOG:    r = log( r );    break;        \
  71.                             case cEXP:    r = exp( r );    break;        \
  72.                             case cSQR:    r = r * r;        break;        \
  73.                             case cRND:    r = r * ( (float) rand() ) / ( (float) RAND_MAX ); break;    \
  74.                             case cSQWV:    r = ( r >= -1.0 && r <= 1.0 ) ? 1 : 0;    break;    \
  75.                             case cTRWV: r = fabs( .5 * r ); r = 2 * ( r - floor( r ) );  if ( r > 1 ) r = 2 - r;    break;    \
  76.                             case cPOS:    if ( r < 0 ) r = 0;            break;        \
  77.                             case cSEED: i = *((long*) &r);                        \
  78.                                         size = i % 31;                            \
  79.                                         srand( ( i << size ) | ( i >> ( 32 - size ) )  );     break;                \
  80.                             case cCLIP:    if ( r < 0 ) r = 0; else if ( r > 1 ) r = 1; break;    \
  81.                         }
  82.                 
  83.         
  84. #define _exeOp( r1, r2 )     switch ( subop ) {                        \
  85.                                 case '+':    r1 += r2;        break;    \
  86.                                 case '-':    r1 -= r2;        break;    \
  87.                                 case '/':    r1 /= r2;        break;    \
  88.                                 case '*':    r1 *= r2;        break;    \
  89.                                 case '^':    r1 = pow( r1, r2 );                    break;    \
  90.                                 case '%':    r1 = ( (long) r1 ) % ( (long) r2 );    break;    \
  91.                             }
  92.         
  93. ExprVirtualMachine::ExprVirtualMachine() {
  94.     mPCStart    = NULL;
  95.     mPCEnd        = NULL;
  96.  
  97. }
  98.                 
  99.                         
  100. int ExprVirtualMachine::FindGlobalFreeReg() {
  101.     int reg = 1;
  102.     
  103.     // Look for a global free register 
  104.     while ( ( mRegColor[ reg ] & REG_USED ) && reg < NUM_REGS )
  105.         reg++;
  106.     
  107.     
  108.     return reg;
  109. }                
  110.         
  111.  
  112. int ExprVirtualMachine::AllocReg() {
  113.     int reg = 0;
  114.     
  115.     // Look for a free register (ie, find one not in use right now)...
  116.     while ( ( mRegColor[ reg ] & REG_IN_USE ) && reg < NUM_REGS )
  117.         reg++;
  118.     
  119.     // Color it
  120.     if ( reg < NUM_REGS )
  121.         mRegColor[ reg ] = REG_IN_USE | REG_USED;
  122.     
  123.     return reg;
  124. }
  125.  
  126.  
  127.  
  128. void ExprVirtualMachine::DeallocReg( int inReg ) {
  129.  
  130.     // Clear the bit that says the reg is allocated
  131.     mRegColor[ inReg ] &= ~REG_IN_USE;
  132. }
  133.  
  134.             
  135. void ExprVirtualMachine::DoOp( int inReg, int inReg2, char inOpCode ) {
  136.  
  137.     __addInst( OP_OPER, ( inOpCode << 16 ) | ( inReg2 << 8 ) | inReg )
  138. }
  139.  
  140.  
  141.         
  142.  
  143.  
  144. void ExprVirtualMachine::Move( int inReg, int inDestReg ) {
  145.     
  146.     if ( inDestReg != inReg ) {
  147.         __addInst( OP_MOVE, ( inReg << 8 ) | inDestReg )    
  148.     }
  149. }
  150.     
  151.  
  152.     
  153.  
  154. void ExprVirtualMachine::Loadi( float inVal, int inReg ) {
  155.     
  156.     __addInst( OP_LOADIMMED, inReg )
  157.     mProgram.Append( &inVal, sizeof( float ) );
  158. }
  159.  
  160.  
  161. void ExprVirtualMachine::Loadi( float* inVal, int inReg ) {
  162.     
  163.     __addInst( OP_LOAD, inReg )
  164.     mProgram.Append( &inVal, 4 );
  165. }    
  166.  
  167.  
  168.  
  169. void ExprVirtualMachine::UserFcnOp( int inReg, float** inFcnH, long inSize ) {
  170.     unsigned short size = inSize;
  171.     
  172.     if ( inFcnH ) {
  173.         __addInst( OP_USER_FCN, inReg )
  174.         mProgram.Append( &size, 2 );
  175.         mProgram.Append( &inFcnH, 4 );  }
  176.     else
  177.         Loadi( 0.0, inReg );
  178. }
  179.  
  180.     
  181.     
  182. void ExprVirtualMachine::MathOp( int inReg, char inFcnCode ) {
  183.     
  184.     if ( inFcnCode ) {
  185.         __addInst( OP_MATHOP, ( inFcnCode << 16 ) | inReg )
  186.     }
  187. }        
  188.  
  189.  
  190.  
  191.  
  192.  
  193.  
  194.  
  195. void ExprVirtualMachine::Clear() {
  196.  
  197.     // Init register coloring
  198.     for ( int i = 0; i < NUM_REGS; i++ )
  199.         mRegColor[ i ] = 0;
  200.         
  201.     mProgram.Wipe();
  202. }
  203.  
  204.  
  205.  
  206. void ExprVirtualMachine::PrepForExecution() {
  207.     mPCStart    = mProgram.getCStr();
  208.     mPCEnd        = mPCStart + mProgram.length();
  209. }
  210.  
  211.                         
  212. // An inst looks like:
  213. // 0-7:     Inst opcode
  214. // 8-15:    Sub opcode
  215. // 16-23:    Source Reg
  216. // 24-31:    Dest Register number
  217.                                 
  218. float ExprVirtualMachine::Execute/*_Inline*/() {
  219.     register float    FR0, FR1, FR2, FR3, FR4, FR5, FR6, FR7; // FR8, FR9, FR10, FR11, FR12, FR13, FR14, FR15;
  220.     register float    v1, v2;
  221.     const char*    PC    = mPCStart;
  222.     const char*    end    = mPCEnd;
  223.     unsigned long    inst, opcode, subop, size, i;
  224.     float*            fcnBase;
  225.     float            mVirtFR[ NUM_REGS - NUM_PHYS_REGS ];
  226.     
  227.     while ( PC < end ) {
  228.         inst = *((long*) PC);    
  229.         PC += 4;
  230.  
  231.         opcode = inst & 0xFF000000;    
  232.         
  233.         if ( opcode == OP_LOADIMMED ) {
  234.             v1 = *((float*) PC);
  235.             PC += 4; }
  236.         else if ( opcode == OP_LOAD ) {
  237.             v1 = **((float**) PC);
  238.             PC += 4; }
  239.         else {
  240.             _fetch( inst & 0xFF, v1 )
  241.             _fetch( ( inst >> 8 ) & 0xFF, v2 )
  242.             subop = ( inst >> 16 ) & 0xFF;
  243.             
  244.             switch ( opcode ) {
  245.                 case OP_OPER:                        
  246.                     _exeOp( v1, v2 )
  247.                     break;
  248.             
  249.                 case OP_MATHOP:
  250.                     _exeFn( v1 )
  251.                     break;
  252.                     
  253.                 case OP_USER_FCN:
  254.                     size = *( (unsigned short*) PC );
  255.                     PC += 2;
  256.                     fcnBase = **((float***) PC);
  257.                     PC += 4;
  258.                     i = v1 * size;
  259.                     if ( i < 0 )
  260.                         i = 0;
  261.                     if ( i >= size ) 
  262.                         i = size - 1;
  263.                     v1 = fcnBase[ i ];
  264.                     break;
  265.                     
  266.                 case OP_MOVE:
  267.                     v1 = v2;
  268.                     break;
  269.                     
  270.                 case OP_WEIGHT:
  271.                     float weight = **((float**) PC);    
  272.                     PC += 4;
  273.                     v1 = ( 1.0 - weight ) * v1 + weight * v2;
  274.                     break;
  275.             }
  276.         }
  277.         
  278.         _store( inst & 0xFF, v1 )
  279.     }
  280.     
  281.     return FR0;
  282. }
  283.  
  284.  
  285.  
  286.  
  287. void ExprVirtualMachine::Chain( ExprVirtualMachine& inVM, float* inTransitionLink ) {
  288.     int tempReg = inVM.FindGlobalFreeReg();
  289.     
  290.     // Move the output of this VM to a reg that won't get overwritten by inVM
  291.     Move( 0, tempReg );
  292.     
  293.     // Now execute inVM (we know it won't touch tempReg)
  294.     mProgram.Append( inVM.mProgram );
  295.     
  296.     // Use the special weight op that combines the two outputs via an embedded addr to a 0 to 1 value
  297.     // Note that the output is moved to register 0
  298.     __addInst( OP_WEIGHT, ( tempReg << 8 ) | 0 )
  299.     mProgram.Append( &inTransitionLink, 4 );
  300.     
  301.     // The reg coloring for this VM is the OR of the two's coloring
  302.     for ( int i = 0; i < NUM_REGS; i++ )
  303.         mRegColor[ i ] |= inVM.mRegColor[ i ];    
  304.         
  305.     PrepForExecution();
  306. }
  307.  
  308.  
  309.  
  310. void ExprVirtualMachine::Assign( ExprVirtualMachine& inExpr ) {
  311.  
  312.     mProgram.Assign( inExpr.mProgram );
  313.     
  314.     for ( int i = 0; i < NUM_REGS; i++ )
  315.         mRegColor[ i ] = inExpr.mRegColor[ i ];
  316.         
  317.     PrepForExecution();
  318. }
  319.  
  320.  
  321.  
  322. /*
  323. void ExprVirtualMachine::Neg() {
  324.     
  325.     __addInst( OP_NEG, 0 )
  326. }        
  327. */
  328.  
  329. /*            
  330. void ExprVirtualMachine::Optimize() {
  331.     char*        base = mProgram.getCStr();
  332.     long*        PC    = (long*) mProgram.getCStr();
  333.     long*        end    = (long*) (((char*) PC) + mProgram.length());
  334.     long        reg, opcode;
  335.     long*        start;
  336.     
  337.     while ( PC < end ) {
  338.         opcode = (*PC) & 0xFF000000;
  339.         start = PC;
  340.         PC++;
  341.         
  342.         // Maintain the PC
  343.         if ( opcode == OP_LOADIMMED )
  344.             PC += 2;
  345.         else if (  opcode == OP_LOAD )
  346.             PC++;
  347.             
  348.         // Look for a 'Load into r0, <Math op>, Move from r0' sequence
  349.         if ( opcode == OP_LOADIMMED || opcode == OP_LOAD ) {
  350.             opcode = (*PC) & 0xFF000000;
  351.             if ( opcode == OP_MOVE_FR0 ) {
  352.                 reg = *PC & 0xFF;                                        // Extract the final dest register
  353.                 *start = (*start) | reg;                                // Change the load so it loads right into the reg it needs to
  354.                 mProgram.Remove( 1 + ( ((char*) PC) - base ), 4 ); }    // Delete the move from fr0 inst
  355.                 // ??
  356.             else if ( opcode == OP_MATHOP ) {
  357.                 opcode = (*(PC + 1)) & 0xFF000000;
  358.                 if ( opcode == OP_MOVE_FR0 ) {
  359.                     reg = *(PC + 1) & 0xFF;                                // Extract the final dest register
  360.                     *start = (*start) | reg;                            // Change the load so it loads right into the reg it needs to
  361.                     *PC = (*PC) | reg;                                    // Change the math op so it operates on the proper reg (see above)
  362.                     mProgram.Remove( 5 + ( ((char*) PC) - base ), 4 );    // Delete the move from fr0 inst
  363.                 } 
  364.             }  // ??
  365.         }
  366.     }    
  367.     
  368.     // Minimzing pushes/pops via stack analysis
  369.     StackReduction( 0, mProgram.length() );
  370. }        
  371.  
  372.  
  373.  
  374.  
  375. long ExprVirtualMachine::StackReduction( long inStartPC, long inEndPC ) {
  376.     long regsInUse = 0, opcode, fcnDepth = 0, pushLoc, reg, regsToPush;
  377.     long PC = inStartPC, progLen, subRegs, *inst;
  378.     char* base = mProgram.getCStr();
  379.     
  380.     while ( PC < inEndPC ) {
  381.         reg = *((long*) (PC + base));
  382.         opcode = reg & 0xFF000000;        // Extract the opcode
  383.         reg &= 0xFF;                    // Extract the dest reg
  384.         
  385.         // We're only interested in root level pop/pushes (ie, when fcnDepth == 0)
  386.         switch ( opcode ) {
  387.             
  388.             case OP_MASSPUSH:
  389.                 if ( fcnDepth == 0 ) {
  390.                     pushLoc = PC;
  391.                     regsInUse = reg;    // We know what's in use by what the compiler wanted us to push
  392.                 }
  393.                 fcnDepth++;
  394.                 break;
  395.             
  396.             case OP_MASSPOP:
  397.                 fcnDepth--;
  398.                 break;
  399.                 
  400.             case OP_LOADIMMED:
  401.                 PC += 4;
  402.             case OP_LOAD:
  403.                 PC += 4;
  404.         }
  405.                 
  406.         // see what regs are in use--skip over insts not at the root level
  407.         if ( fcnDepth == 0 ) {
  408.             switch ( opcode ) {
  409.                 
  410.                 case OP_OPER:
  411.                     regsInUse |= ( 2 << reg );
  412.                 case OP_LOADIMMED:
  413.                 case OP_LOAD:
  414.                 case OP_MATHOP:
  415.                 case OP_MOVEUP:
  416.                 case OP_MOVE_FR0:
  417.                     regsInUse |= ( 1 << reg );
  418.                 break;
  419.                     
  420.                 // Catch the leaving a fcn at the root level
  421.                 case OP_MASSPOP:
  422.                 
  423.                     // Get the regs that get sub-used (ie, used between pushLoc and PC)
  424.                     progLen = mProgram.length();
  425.                     subRegs = StackReduction( pushLoc + 4, PC );
  426.                     
  427.                     // StackReduction() may have elminated instructions, so adjust our PC
  428.                     PC        -= progLen - mProgram.length();
  429.                     inEndPC -= progLen - mProgram.length();
  430.                     
  431.                     // Reassign what regs get pushed then popped.  We must push the regs that get used in the sub fcn and we use here
  432.                     regsToPush = subRegs & regsInUse;
  433.                     if ( regsToPush ) {
  434.                         inst = (long*) (base + pushLoc);
  435.                         *inst = OP_MASSPUSH | regsToPush;        // Reassign the push
  436.                         inst = (long*) (base + PC);
  437.                         *inst = OP_MASSPOP | regsToPush; }        // Reassign the pop
  438.                     
  439.                     // If no regs need to get pushed, delete the pop and push insts
  440.                     else {
  441.                         mProgram.Remove( PC + 1, 4 );
  442.                         mProgram.Remove( pushLoc + 1, 4 );
  443.                         PC        -= 8;
  444.                         inEndPC    -= 8;
  445.                     }
  446.                     break;
  447.             }
  448.         }
  449.         
  450.         // Move the PC along, for we just looked at an instruction
  451.         PC += 4;
  452.     }
  453.     
  454.     return regsInUse;
  455. }
  456.  
  457. */
  458.  
  459.  
  460.  
  461.